﻿using System;
using System.Collections.Generic;
using System.Data.Entity.ModelConfiguration;
using System.Data.Entity.ModelConfiguration.Configuration;
using System.Linq.Expressions;
using System.Reflection;

namespace Emico.Data.Entity.ModelConfiguration
{
    public static class EntityConfigurationExtensions
    {
        public static StringPropertyConfiguration Property<T>(this EntityTypeConfiguration<T> mapper, String propertyName) where T : class
        {
            var expression = CreateExpression<T, String>(propertyName);
            return mapper.Property(expression);
        }

        public static PrimitivePropertyConfiguration Property<T, K>(this EntityTypeConfiguration<T> mapper, String propertyName)
            where T : class
            where K : struct
        {
            var expression = CreateExpression<T, K>(propertyName);
            return mapper.Property(expression);
        }

        public static EntityTypeConfiguration<T> HasKey<T, K>(this EntityTypeConfiguration<T> mapper, String propertyName)
            where T : class
            where K : struct
        {
            var expression = CreateExpression<T, K>(propertyName);
            return mapper.HasKey(expression);
        }

        private static Expression<Func<T, K>> CreateExpression<T, K>(String propertyName)
        {
            var type = typeof(T);
            var arg = Expression.Parameter(type, "x");
            Expression expr = arg;

            var pi = type.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
            if (pi == null)
            {
                throw new ArgumentException(String.Format("Property '{0}' on type '{1}' cannot be found.", propertyName, type.FullName));
            }

            expr = Expression.Property(expr, pi);
            LambdaExpression lambda = Expression.Lambda(expr, arg);

            var expression = (Expression<Func<T, K>>)lambda;
            return expression;
        }

        public static ManyNavigationPropertyConfiguration<T, U> HasMany<T, U>(this EntityTypeConfiguration<T> mapper,  String propertyName)
            where T : class
            where U : class
        {
            Type type = typeof(T);
            ParameterExpression arg = Expression.Parameter(type, "x");
            Expression expr = arg;

            PropertyInfo pi = type.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
            if (pi == null)
            {
                throw new ArgumentException(String.Format("Many navigation property '{0}' on type ICollection<'{1}'> cannot be found.", propertyName, type.FullName));
            }

            expr = Expression.Property(expr, pi);
            LambdaExpression lambda = Expression.Lambda(expr, arg);

            Expression<Func<T, ICollection<U>>> expression = (Expression<Func<T, ICollection<U>>>)lambda;
            return mapper.HasMany(expression);
        }
    }
}
